(* Copyright 2007 dnAnalytics Project.
 *
 * Contributors to this file:
 * Jurgen Van Gael
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this 
 *   list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice, 
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * * Neither the name of the dnAnalytics Project nor the names of its contributors
 *   may be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *)
#light

namespace dnAnalytics.LinearAlgebra

open dnAnalytics.LinearAlgebra

/// A module which implements functional vector operations.
module Vector =
        
    /// Transform a vector into an array.
    let inline to_array (v: #Vector) =
        let n = v.Count
        Array.init n (fun i -> v.Item(i))
        
    /// Transform a vector into an array.
    let inline to_list (v: #Vector) =
        let n = v.Count
        List.init n (fun i -> v.Item(i))

    /// In-place mutation by applying a function to every element of the vector.
    let inline inplace_map (f: float -> float) (v: #Vector) =
        for i=0 to v.Count-1 do
            v.Item(i) <- f (v.Item(i))
        ()

    /// In-place mutation by applying a function to every element of the vector.
    let inline inplace_mapi (f: int -> float -> float) (v: #Vector) =
        for i=0 to v.Count-1 do
            v.Item(i) <- f i (v.Item(i))
        ()
        
    /// In-place scaling of vector components.
    let inline inplace_scale (f: float) (v: #Vector) =
        for i=0 to v.Count-1 do
            v.Item(i) <- f * v.Item(i)
        ()
    
    /// In-place assignment.
    let inline inplace_assign (f: int -> float) (v: #Vector) =
        for i=0 to v.Count-1 do
            v.Item(i) <- f i
        ()
        
    /// In-place vector addition.
    let inline inplace_add (v: #Vector) (w: #Vector) =
        v.Add w
        
    /// In place vector subtraction.
    let inline inplace_sub (v: #Vector) (w: #Vector) =
        v.Subtract w
    
    /// Functional map operator for vectors.
    /// <include file='../../../../FSharpExamples/DenseVector.xml' path='example'/> 
    let inline map f (v: #Vector) =
        let w = v.Clone()
        inplace_mapi (fun _ x -> f x) w
        w
        
    /// Applies a function to all elements of the vector.
    let inline iter (f: float -> unit) (v: #Vector) =
        for i=0 to v.Count-1 do
            f (v.Item i)
        
    /// Applies a function to all elements of the vector.
    let inline iteri (f: int -> float -> unit) (v: #Vector) =
        for i=0 to v.Count-1 do
            f i (v.Item i)
            
    /// Maps a vector to a new vector by applying a function to every element.
    let inline mapi (f: int -> float -> float) (v: #Vector) =
        let w = v.Clone()
        inplace_mapi f w
        w

    /// Fold all entries of a vector.
    let inline fold (f: 'a -> float -> 'a) (acc0: 'a) (v: #Vector) =
        let mutable acc = acc0
        for i=0 to v.Count-1 do
            acc <- f acc (v.Item(i))
        acc

    /// Fold all entries of a vector using a position dependent folding function.
    let inline foldi (f: int -> 'a -> float -> 'a) (acc0: 'a) (v: #Vector) =
        let mutable acc = acc0
        for i=0 to v.Count-1 do
            acc <- f i acc (v.Item(i))
        acc
        
    /// Checks whether a predicate is satisfied for every element in the vector.
    let inline forall (p: float -> bool) (v: #Vector) =
        let mutable b = true
        let mutable i = 0
        while b && i < v.Count do
            b <- b && (p (v.Item(i)))
            i <- i+1
        b
    
    /// Checks whether there is an entry in the vector that satisfies a given predicate.
    let inline exists (p: float -> bool) (v: #Vector) =
        let mutable b = false
        let mutable i = 0
        while not(b) && i < v.Count do
            b <- b || (p (v.Item(i)))
            i <- i+1
        b
    
    /// Checks whether a predicate is true for all entries in a vector.
    let inline foralli (p: int -> float -> bool) (v: #Vector) =
        let mutable b = true
        let mutable i = 0
        while b && i < v.Count do
            b <- b && (p i (v.Item(i)))
            i <- i+1
        b
    
    /// Checks whether there is an entry in the vector that satisfies a given position dependent predicate.
    let inline existsi (p: int -> float -> bool) (v: #Vector) =
        let mutable b = false
        let mutable i = 0
        while not(b) && i < v.Count do
            b <- b || (p i (v.Item(i)))
            i <- i+1
        b

    /// Scans a vector; like fold but returns the intermediate result.
    let inline scan_left (f: float -> float -> float) (v: #Vector) =
        let w = v.Clone()
        let mutable p = v.Item(0)
        for i=1 to v.Count-1 do
            p <- f p (v.Item(i))
            w.[i] <- p
        w

    /// Scans a vector; like fold but returns the intermediate result.
    let inline scan_right (f: float -> float -> float) (v: #Vector) =
        let w = v.Clone()
        let mutable p = v.Item(v.Count-1)
        for i=2 to v.Count do
            p <- f (v.Item(v.Count - i)) p
            w.[v.Count - i] <- p
        w

    /// Reduces a vector: the result of this function will be f(...f(f(v[0],v[1]), v[2]),..., v[n]).
    let inline reduce_left (f: float -> float -> float) (v: #Vector) =
        let mutable p = v.Item(0)
        for i=1 to v.Count-1 do
            p <- f p (v.Item(i))
        p

    /// Reduces a vector: the result of this function will be f(v[1], ..., f(v[n-2], f(v[n-1],v[n]))...).
    let inline reduce_right (f: float -> float -> float) (v: #Vector) =
        let mutable p = v.Item(v.Count-1)
        for i=2 to v.Count do
            p <- f (v.Item(v.Count - i)) p
        p